import { Inject, Provide } from '@midwayjs/core';
import { Context } from '@midwayjs/koa';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository, In, Not } from 'typeorm';
import { UserEntity } from '@entity/system/user.entity';
import { UserPostEntity } from '@entity/system/userPost.entity';
import { UserRoleEntity } from '@entity/system/userRole.entity';
import { DeptEntity } from '@entity/system/dept.entity';
import { PostEntity } from '@entity/system/post.entity';
import { RoleEntity } from '@entity/system/role.entity';
import * as path from 'path';
import * as fs from 'fs/promises';
import { makeHttpRequest } from '@midwayjs/core';
import wxConfig from '@utils/wxConfig';
import methods from '@/utils/methods';
import { JwtService } from '@midwayjs/jwt';

import {
  ListUserDTO,
  CreateUserDTO,
  UpdateUserDTO,
  UpdatePwdDto,
  ResetPwdDto,
  ChangeStatusDto,
  UpdateProfileDto,
  UpdateAuthRoleDTO,
  CreateWxUserDTO,
  WxLoginInfo,
} from '@dto/system/user.dto';
import { ImportExcelDTO } from '@dto/common/excel.dto';
import { DownloadExcelService } from '@service/common/downloadExcel';
import { ResolveExcelService } from '@service/common/resolveWebExcel';
import { resBuild } from '@utils/resBuild';
import { checkKeyIsExist } from '@utils/serviceHelp';
import { getOperator, deepClone } from '@utils';
import { genHashPsw, isEqualPsw } from '@utils/password';
import { UploadFileInfo } from '@midwayjs/busboy';
import { LoginService } from '@/service/login.service';
import { LoginDTO } from '@dto/login.dto';

@Provide()
export class UserService {
  @Inject()
  ctx: Context;

  @Inject()
  jwtService: JwtService;

  @Inject()
  protected downloadExcelService: DownloadExcelService;

  @Inject()
  protected LoginService: LoginService;

  @Inject()
  protected resolveExcelService: ResolveExcelService;

  @InjectEntityModel(UserEntity)
  protected userEntity: Repository<UserEntity>;

  @InjectEntityModel(DeptEntity)
  protected deptEntity: Repository<DeptEntity>;

  @InjectEntityModel(PostEntity)
  protected postEntity: Repository<PostEntity>;

  @InjectEntityModel(RoleEntity)
  protected roleEntity: Repository<RoleEntity>;

  @InjectEntityModel(UserPostEntity)
  protected userPostEntity: Repository<UserPostEntity>;

  @InjectEntityModel(UserRoleEntity)
  protected userRoleEntity: Repository<UserRoleEntity>;

  // 列表
  async list(queryParams: ListUserDTO) {
    const queryBuilder = this.userEntity
      .createQueryBuilder('entity')
      .leftJoinAndSelect('entity.dept', 'dept');

    if (queryParams.userName) {
      queryBuilder.andWhere(`entity.userName LIKE "%${queryParams.userName}%"`);
    }
    if (queryParams.phonenumber) {
      queryBuilder.andWhere(
        `entity.phonenumber LIKE "%${queryParams.phonenumber}%"`
      );
    }
    if (queryParams.status) {
      queryBuilder.andWhere(`entity.status = ${queryParams.status}`);
    }
    // 时间范围，包含全天
    if (queryParams['params[beginTime]'] && queryParams['params[endTime]']) {
      queryBuilder.andWhere(
        'entity.createTime BETWEEN :beginTime AND :endTime',
        {
          beginTime: queryParams['params[beginTime]'] + ' 00:00:00',
          endTime: queryParams['params[endTime]'] + ' 23:59:59',
        }
      );
    }

    if (queryParams.deptId) {
      queryBuilder.orWhere('dept.deptId = :deptId', {
        deptId: queryParams.deptId,
      });
      queryBuilder.orWhere('FIND_IN_SET(:ancestors, dept.ancestors) > 0', {
        ancestors: String(queryParams.deptId),
      });
    }

    if (queryParams.pageNum && queryParams.pageSize) {
      queryBuilder
        .skip((queryParams.pageNum - 1) * queryParams.pageSize)
        .take(queryParams.pageSize);
    }

    const [rows, total] = await queryBuilder.getManyAndCount();
    return resBuild.list(rows, total);
  }

  // 添加
  async create(user: CreateUserDTO) {
    // 新增之前先判断是否已存在该
    await checkKeyIsExist(this.userEntity, 'userName', user.userName);

    // 根据明文密码生成hash密码
    user.password = await genHashPsw(user.password);

    // 新增用户
    const myEntity = this.userEntity.create(user);
    myEntity.setCreateBy(getOperator(this.ctx));
    await this.userEntity.save(myEntity);

    return resBuild.success();
  }

  // 添加微信用户
  async createWx(queryParams: CreateWxUserDTO) {
    const result = await makeHttpRequest(
      'https://api.weixin.qq.com/sns/jscode2session',
      {
        data: {
          appid: wxConfig.AppID,
          secret: wxConfig.AppSecret,
          js_code: queryParams.code,
          grant_type: 'authorization_code',
        },
        dataType: 'json',
      }
    );
    const wxInfo: WxLoginInfo = result.data;

    // 新增之前先判断是否已存在该
    const isExist: ListUserDTO = await this.userEntity.findOneBy({
      ['openId']: wxInfo.openid,
    });
    if (isExist) {
      let loginBody: LoginDTO = {
        username: isExist.openId,
        password: '123456',
        code: '',
      };
      let loginIndf = await this.LoginService.login(loginBody);
      return resBuild.more(loginIndf);
    } else {
      const user: CreateUserDTO = new CreateUserDTO();
      // 根据明文密码生成hash密码
      user.password = await genHashPsw('123456');
      // 用户名称
      user.nickName = '陌路侠士' + methods.getRandomString(6);
      // 用户账号
      user.userName = wxInfo.openid;
      // 微信openId
      user.openId = wxInfo.openid;
      console.log(user);
      // 新增用户
      const myEntity = this.userEntity.create(user);
      myEntity.setCreateBy(user.nickName);
      await this.userEntity.save(myEntity);
      let loginBody: LoginDTO = {
        username: wxInfo.openid,
        password: '123456',
        code: '',
      };
      let loginIndf = await this.LoginService.login(loginBody);

      return resBuild.more(loginIndf);
    }
  }

  // 删除：删除用户时，需要同时删除用户岗位关联，用户角色关联
  async delete(userId: string) {
    // 构建ids数组
    const ids: number[] = userId.split(',').map(id => Number(id));

    // 删除用户
    await this.userEntity.delete(ids);

    // 删除用户岗位关联
    await this.userPostEntity.delete({ userId: In(ids) });

    // 删除用户角色关联
    await this.userRoleEntity.delete({ userId: In(ids) });

    return resBuild.success();
  }

  // 修改
  async update(user: UpdateUserDTO) {
    // 执行更新
    const myEntity = this.userEntity.create(user);
    myEntity.setUpdateBy(getOperator(this.ctx));
    await this.userEntity.save(myEntity);
    return resBuild.success();
  }

  // 详情
  async detail(userId: number | undefined) {
    // 获取所有岗位
    const postList = await this.postEntity.find();
    // 获取所有角色，超管除外(不给勾选)
    const roleList = await this.roleEntity.find({
      where: {
        roleId: Not(1),
      },
    });

    if (!userId) {
      // 对应接口：/system/user/
      // 如果userId为空，新增用户的时候用，只返回posts和roles列表作下拉
      return resBuild.more({
        posts: postList,
        roles: roleList,
      });
    } else {
      // 对应接口：/system/user/:userId
      const detailInfo = await this.userEntity.findOne({
        where: {
          userId: userId,
        },
        relations: ['dept', 'roles', 'posts'],
      });

      // 用户所拥有的岗位
      const postIds = await this.userPostEntity.find({
        where: {
          userId: userId,
        },
      });
      // 用户所拥有的角色
      const roleIds = await this.userRoleEntity.find({
        where: {
          userId: userId,
        },
      });

      return resBuild.more({
        data: detailInfo,
        postIds: postIds.map(item => item.postId),
        roleIds: roleIds.map(item => item.roleId),
        posts: postList,
        roles: roleList,
      });
    }
  }

  async userInfo() {
    // 从请求头或请求参数中获取token
    const token = this.ctx.headers.authorization?.split(' ')[1]; // 假设token在Authorization头中，格式为Bearer <token>
    const decoded: any = await this.jwtService.verify(token, {
      complete: true,
    });
    let userId = decoded.payload.userId;

    const userInfo: ListUserDTO = await this.userEntity.findOneBy({
      ['userId']: userId,
    });
    return resBuild.more({
      userInfo: userInfo,
    });
  }

  // 导出
  async export(queryParams: ListUserDTO) {
    // 默认导出全部，去掉分页参数
    delete queryParams.pageNum;
    delete queryParams.pageSize;
    const headers = [
      { label: '用户编号', prop: 'userId' },
      { label: '用户名称', prop: 'userName' },
      { label: '用户昵称', prop: 'nickName' },
      { label: '部门', prop: 'dept.deptName' },
      { label: '手机号码', prop: 'phonenumber' },
      { label: '状态', prop: 'status' },
      { label: '创建时间', prop: 'createTime', width: 25 },
    ];
    const { rows } = await this.list(queryParams);
    return this.downloadExcelService.downloadExcel({
      headers: headers,
      data: rows,
      sheetName: '用户信息',
    });
  }

  // 下载导入模板
  async importTemplate() {
    const headers = [
      { label: '部门编号', prop: 'deptId' },
      { label: '登录名称', prop: 'userName' },
      { label: '用户昵称', prop: 'nickName' },
      { label: '用户邮箱', prop: 'email' },
      { label: '手机号码', prop: 'phonenumber' },
      { label: '用户性别', prop: 'sex' },
      { label: '账号状态', prop: 'status' },
    ];
    return this.downloadExcelService.downloadExcel({
      headers: headers,
      data: [],
      sheetName: '用户导入模板',
    });
  }

  // 导入 下载的模板
  async importData(body: ImportExcelDTO) {
    const { files, updateSupport } = body;
    // 1.先解析模板
    const result = await this.resolveExcelService.resolvedWebExcelStream(files);
    // 2.获取模板的数据
    const { tableData } = result;
    // 3.循环校验数据，校验通过就插入数据库，校验失败就记录下来，最后返回给前端
    const tipMsg = await this.resolveExcelService.analysisExcelAndWhite({
      tableData,
      updateSupport,
      entityName: this.userEntity,
      headerFields: [
        'deptId',
        'userName',
        'nickName',
        'email',
        'phonenumber',
        'sex',
        'status',
      ],
      uniqueKey: 'userName',
    });
    return resBuild.success(tipMsg);
  }

  // 表格 - 重置密码
  async resetPsw(body: ResetPwdDto) {
    if (body.userId === 1) {
      throw new Error('超级管理员不允许重置密码');
    }
    if (body.password) {
      body.password = genHashPsw(body.password);
    }
    await this.userEntity.update(
      {
        userId: body.userId,
      },
      {
        password: body.password,
      }
    );
    return resBuild.success('重置成功');
  }

  // 表格 - 修改用户状态
  async changeStatus(body: ChangeStatusDto) {
    return resBuild.success('开发中');
  }

  // 表格 - 分配角色信息及列表
  async authRoleList(userId: number) {
    const detailInfo = await this.userEntity.findOne({
      where: {
        userId: userId,
      },
      relations: ['dept', 'roles', 'posts'],
    });
    const roleList: any = await this.roleEntity.find({
      where: {
        roleId: Not(1), // 勾选的角色列表要排除超管
      },
    });
    // 如果已有某个角色，打个标记flag=true，返回给前端
    roleList.forEach((item: any) => {
      item.flag = detailInfo.roles.some(role => role.roleId === item.roleId);
    });
    return resBuild.more({
      user: detailInfo,
      roles: roleList,
    });
  }

  // 分配角色，提交
  async authRoleUpdate(body: UpdateAuthRoleDTO) {
    // 先判断原先用户与角色是否存在关联
    const isHasRelation = await this.userRoleEntity.findBy({
      userId: body.userId,
    });

    // 如果有关联，先删除原有的用户和角色的关联
    if (isHasRelation) {
      await this.userRoleEntity.delete({
        userId: body.userId,
      });
    }

    // 然后重新建立关联
    const roleIds = body.roleIds.split(',').map(item => Number(item));
    await this.userConnectWithRole(body.userId, roleIds);
    return resBuild.success();
  }

  // 个人中心 - 获取个人信息
  async profile() {
    const userId = this.ctx.session.userInfo.userId;
    // 获取用户信息、关联：角色、岗位信息
    const userInfo = await this.userEntity.findOne({
      where: {
        userId,
      },
      relations: ['dept', 'roles', 'posts'],
    });
    const postNames = userInfo.posts.map(item => item.postName);
    const roleNames = userInfo.roles.map(item => item.roleName);
    return resBuild.more({
      data: userInfo,
      postGroup: postNames.join(),
      roleGroup: roleNames.join(),
    });
  }

  async profileAvatar(file: UploadFileInfo) {
    try {
      // 1. 校验文件基础信息
      if (!file || !file[0] || !file[0].data) {
        throw new Error('上传文件信息不完整');
      }

      // 2. 处理原始文件名与路径
      const originFileName = deepClone(file[0].data);
      const normalizedPath = path.normalize(originFileName);
      const pureFileName = normalizedPath.replace(/\\/g, '/');

      // 3. 生成「年/月/日」文件夹（当前日期）
      const dateFolder = new Date()
        .toISOString()
        .slice(0, 10)
        .replace(/-/g, '/');
      // 示例：2025/08/19

      // 4. 拼接最终保存路径（示例：public/uploads/2025/08/19/xxx.png）
      const uploadRoot = path.join(process.cwd(), 'public', 'uploads');
      const targetDir = path.join(uploadRoot, dateFolder);
      const targetPath = path.join(targetDir, path.basename(pureFileName));

      // 5. 递归创建「年/月/日」文件夹
      await fs.mkdir(targetDir, { recursive: true });
      const fileBuffer = await fs.readFile(file[0].data);
      await fs.writeFile(targetPath, fileBuffer);

      const publicBase = '/public/uploads/';
      const accessUrl = path.join(
        publicBase,
        dateFolder,
        path.basename(pureFileName)
      );
      return resBuild.more({ imgUrl: accessUrl.replace(/\\/g, '/') });
    } catch (err) {
      console.error('处理头像上传失败:', err);
      throw new Error('头像上传失败，请重试');
    }
  }

  // 个人中心 - 修改密码
  async updatePwd(updatePsw: UpdatePwdDto) {
    const userId = this.ctx.session.userInfo.userId;
    const userInfo = await this.userEntity.findOne({
      where: {
        userId: userId,
      },
    });
    if (!userInfo) {
      throw new Error('用户不存在');
    }
    const isEqual = await isEqualPsw(updatePsw.oldPassword, userInfo.password);
    if (!isEqual) {
      throw new Error('旧密码错误');
    }
    if (updatePsw.oldPassword === updatePsw.newPassword) {
      throw new Error('新密码不能与旧密码相同');
    }
    // 新密码加密
    userInfo.password = genHashPsw(updatePsw.newPassword);
    const updateResult = await this.userEntity.save(userInfo);
    if (updateResult) {
      return resBuild.success('修改成功');
    } else {
      throw new Error('修改失败');
    }
  }

  // 个人中心 - 修改用户信息
  async updateProfile(user: UpdateProfileDto) {
    const myUserInfo: UserEntity = this.ctx.session.userInfo;
    await this.userEntity.update(
      {
        userId: myUserInfo.userId,
      },
      {
        ...user,
      }
    );
    return resBuild.success();
  }

  /**  ------ 下面是辅助函数 ------ */

  // 用户user和岗位post关联
  async userConnectWithPost(userId: number, postIds: number[]) {
    const userPostList = postIds.map(postId => {
      return {
        userId,
        postId,
      };
    });
    await this.userPostEntity.save(userPostList);
  }

  // 用户user和岗位role关联
  async userConnectWithRole(userId: number, roleIds: number[]) {
    const userRoleList = roleIds.map(roleId => {
      return {
        userId,
        roleId,
      };
    });
    await this.userRoleEntity.save(userRoleList);
  }
}
